/**
 * 可以拖动的一个图片组件
 */
import React from 'react';
import './index.less';
import { Tooltip } from 'antd';
import { useDrag, useDrop } from 'react-dnd';
import { DragSourceMonitor, DropTargetMonitor } from 'react-dnd/dist/types/types/monitors';
import { ItemTypes } from './ItemTypes';

// 定义一个类型，为什么要定义这个类型，今后要研究以下
interface DragItem {
  index: number;
  id: string;
  type: string;
}

/**
 * Drag=拖动  Drop=可以放下的
 * @param props
 * @returns
 */
const DndPictureCard = (props: any) => {
  const { originNode, moveRow, file, fileList } = props;
  const ref = React.useRef();
  const currentFileIndex = fileList.indexOf(file);

  /**
   * 一个 hook 函数，可以让一个 DOM 元素能够放置拖拽元素。
   * 返回二个对象：
   *    index 0 collectedProps： 函数返回的对象，如果没有对象可以返回空。
   *    index 1 drop：一个连接器函数，用在 React 的 ref 属性。连接了能够放置的元素。
   * 下面是配置对象元素：
   *    accept必填项，定义了一个可以接收的拖拽元素的类型。（就是 useDrag 中的 type）
   *    collect 收集函数，必须返回一个对象。具体配置参照 DropTargetMonitor，它返回的对象会出现在 index 0 中
   *    drop(item, monitor) 放置 drag 元素事件
   *    ---------------------------不常用-------------------------------------
   *    hover(item, monitor) drag 元素进入 drop 元素事件。
   *    canDrop(item, monitor) 定义是否能否放置的函数。如果使用默认方式，不需要定义。
   *
   */
  const [collectedProps, drop] = useDrop({
    accept: ItemTypes.IMGCARD,
    collect: (monitor: DropTargetMonitor) => {
      const { index: dragIndex } = (monitor.getItem() as DragItem) || {};
      if (dragIndex === currentFileIndex) {
        return {};
      }
      return {
        isOver: monitor.isOver(),
        dropClassName: dragIndex < currentFileIndex ? ' drop-over-downward' : ' drop-over-upward',
      };
    },
    drop: (item: DragItem) => {
      moveRow(item.index, currentFileIndex);
    },
  });

  // 得到useDrop中的变量
  const { isOver, dropClassName } = collectedProps;

  /**
   * 一个 hook 函数，可以让一个 DOM 元素实现拖拽效果。
     返回三个对象：
          index 0 一个对象，它是从配置中的 collect 函数来定义的。
          index 1 一个连接器函数，用在 React 的 ref 属性。连接了能够拖拽的元素。
          index 2 一个连接器函数，用在 React 的 ref 属性。连接了会被拖拽的元素。
          PS; 1 和 2 可以用来实现拖拽元素 A 中的元素 B，元素 A 跟着移动。
    下面是 Hook 函数中的配置对象元素：
          item 必填属性，它是一个纯 JavaScript 对象。这个对象中必须传的是 type 属性。另外的一个作用是传递拓转元素中的数据。可以在拓转完成函数 end 中拿到数据做后续处理。
          type 必填属性，只有 drag 和 drop 中的 type 相才能实现拖放。    
          collect 收集函数，必须返回一个对象。具体配置可参照 DragSourceMonitor，它返回的对象可以在 index 0 对象中拿到。  
          ----------------------其他可以选的--------------------------------  
          canDrag(monitor) 定义能否拖拽的函数。 
          isDragging(monitor) 用于重写定义正在拖拽中的状态。，一般不用改。
          begin(monitor) 拖拽开始事件，一般不需要返回，如果有 return 会替换 item
          end(item, monitor) 拖拽结束事件
          options 配置对象
          previewOptions 预览配置对象？
   */
  const [, drag] = useDrag({
    type: ItemTypes.IMGCARD,
    item: { index: currentFileIndex },
    collect: (monitor: DragSourceMonitor) => ({
      isDragging: monitor.isDragging(),
    }),
  });

  // 将drag与drop关联起来，drag与drop调用的顺序没有严格要求
  drag(drop(ref));
  const errorNode = <Tooltip title="Upload Error">{originNode.props.children}</Tooltip>;
  return (
    <div
      // @ts-ignore
      ref={ref}
      className={`ant-upload-draggable-list-item ${isOver ? dropClassName : ''}`}
      style={{ cursor: 'move' }}
    >
      {file.status === 'error' ? errorNode : originNode}
    </div>
  );
};

export default DndPictureCard;
